package org.Aftpclient;

import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.List;

import org.aftputil.AFtpUtil;

public class AFtpClient 
{
	public static final int PASV = 0x000001;
	public static final int PORT = 0x000001;
	private int  timeout = 6000;
	private int mode = PASV;
	private String curdir = "/";
	private String userName;
	private String passWord;

	private String serverIp = "127.0.0.1";
	private String serverPort = "21";
	private Socket cmdSocket = null;
	private Socket dataSocket = null;
	
	private BufferedReader cmdreader = null;
	private BufferedWriter cmdwriter = null;
	
	private String localPath= "D:\\";

	public AFtpClient()
	{
		
	}
	public AFtpClient(int mode,String userName,String passWord) 
	{
	   this.mode = mode;
	   if(this.mode!=PASV && this.mode!=PORT)
		   this.mode = PASV;
	   
	   this.userName = userName;
	   this.passWord = passWord;
	   
    }
	
	/**
	 * login 
	 * @return
	 */
	synchronized public boolean getLogin() 
	{
		boolean stat = true;
		String cmd = null;
		try
		{
			//create command socket
			this.cmdSocket = new Socket(this.serverIp,Integer.valueOf(serverPort));
			cmdSocket.setSoTimeout(timeout);
		    
			this.cmdreader = new BufferedReader(new InputStreamReader(this.cmdSocket.getInputStream(),"GBK"));
		    this.cmdwriter = new BufferedWriter(new OutputStreamWriter(this.cmdSocket.getOutputStream(),"GBK"));
		    
		    cmd = cmdreader.readLine();
		    print(cmd);
		    if(!cmd.startsWith("220")) //no welcome message
		    	return false; 
		    
		    this.response("USER "+this.userName);
		    cmd = cmdreader.readLine();
		    print(cmd);
		    if(!cmd.startsWith("331")) //username error
		    	return false;
		    
		    
		    this.response("PASS "+this.passWord);
		    cmd = cmdreader.readLine();
		    print(cmd);
		    if(!cmd.startsWith("230"))  //password error
		    	return false;
		    
		    print("login ok");          //login successfully
		    
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return false;
			
		}
		/*finally
		{
			//release the system resouse
			try
			{
			    if(this.cmdSocket !=null) this.cmdSocket.close();
			    if(this.cmdreader!=null)  this.cmdreader.close();
			    if(this.cmdwriter!=null)  this.cmdwriter.close();
			    
			}catch(Exception e)
			{
				e.printStackTrace();
			}
		}*/
		
		return stat;
		
	}
	
	/**
	 * do command LIST in Passive Mode
	 * @param path
	 * @return
	 */
	synchronized public  List pasvList(String path)
	{
		List stat = null;
		Socket datasocket = null;
		BufferedReader datareader = null;
		String cmd = null;
		try
		{
	
			String ipport = pasv();
			System.out.println("pasv return:"+ipport);
			if(ipport == null)
			{
				System.out.println("pasv error ipport == null");
				return stat;
			}
			String [] tmp = ipport.split(" ");
			if(tmp.length!=2) 
			{
				System.out.println("pasv error ipport error");
				for(int i = 0; i < tmp.length; i++)
					System.out.println(tmp[i]);
				return stat;
			}
			String ip = tmp[0];
			int port = Integer.valueOf(tmp[1]);
			
			//send LIST command
			this.response("LIST");
			System.out.println("server sockaddr:"+ip+","+port);
			//connect to server's data socket
		    datasocket = new Socket(ip,port);
			datasocket.setSoTimeout(timeout);
		    datareader = new BufferedReader(new InputStreamReader(datasocket.getInputStream()));
			
			//wait for "150 Opening ASCII mode data connection."
			cmd = this.cmdreader.readLine();
			print(cmd);
			if(!cmd.startsWith("150")) return stat;
			
			stat = new ArrayList<String>();
			String line = null;
			try
			{
				while((line = datareader.readLine()) != null)
				{   
					System.out.println(line);
					stat.add(line);
				}
			}
			catch(EOFException e)
			{
				e.printStackTrace();
			}
			catch(IOException e)
			{
			e.printStackTrace();
			}
			
			cmd = this.cmdreader.readLine(); 
			print(cmd);
			if(!cmd.startsWith("226")) return null;
			
		}
		catch(Exception e)
		{
			e.printStackTrace();
			try 
			{
				if(dataSocket != null )dataSocket.close();
				if(datareader != null )datareader.close();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			return null;
		}
		
		return stat;
	}
	

	/**
	 * do RETR command in Passive Mode
	 * @param fileName
	 * @return
	 */
	synchronized public boolean pasvRETR(String fileName)
	{
		
		boolean stat = true;
		Socket datasocket = null;
		DataInputStream datareader = null;
		DataOutputStream datawriter = null;
		String cmd = null;
		try
		{
	       
			String ipport = pasv();
			System.out.println("pasv return:"+ipport);
			if(ipport == null)
			{
				System.out.println("pasv error ipport == null");
				return false;
			}
			String [] tmp = ipport.split(" ");
			if(tmp.length!=2) 
			{
				System.out.println("pasv error ipport error");
				for(int i = 0; i < tmp.length; i++)
					System.out.println(tmp[i]);
				return false;
			}
			String ip = tmp[0];
			int port = Integer.valueOf(tmp[1]);
			
			//send RETR command
			this.response("RETR "+fileName);
			System.out.println("server sockaddr:"+ip+","+port);
			//connect to server's data socket
		    datasocket = new Socket(ip,port);
			datasocket.setSoTimeout(timeout);
		    datareader = new DataInputStream(datasocket.getInputStream());
			datawriter = new DataOutputStream(new FileOutputStream(this.localPath+"\\"+fileName));
			//wait for "150 Opening ASCII mode data connection."
			cmd = this.cmdreader.readLine();
			print(cmd);
			if(!cmd.startsWith("150")) 
			{
				return false;
			}
			
			//begin read data and write to the local file
			 byte [] buf = new byte[40800];
			 int length = 0;
            while((length = datareader.read(buf)) != -1)
            {
            	datawriter.write(buf, 0, length);
            	datawriter.flush();
            	
            }
			cmd = this.cmdreader.readLine(); 
			print(cmd);
			if(!cmd.startsWith("226")) 
			{	
				return false;
			}
			
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return false;
		}
		finally
		{
			try{
				if(dataSocket != null )dataSocket.close();
				if(datawriter != null )datawriter.close();
				if(datareader != null )datareader.close();
				} catch (IOException e1) {e1.printStackTrace();}
		}
		
		return stat;
	}
	
	
	/**
	 * do RETR command in Passive Mode
	 * @param fileName
	 * @return
	 */
	synchronized public boolean pasvSTOR(String path)
	{
		
		boolean stat = true;
		Socket datasocket = null;
		DataInputStream datareader = null;
		DataOutputStream datawriter = null;
		String cmd = null;
		try
		{
	       
			//pasv command and get the ip and port of server
			String ipport = pasv();
			System.out.println("pasv return:"+ipport);
			if(ipport == null)
			{
				System.out.println("pasv error ipport == null");
				return false;
			}
			String [] tmp = ipport.split(" ");
			if(tmp.length!=2) 
			{
				System.out.println("pasv error ipport error");
				for(int i = 0; i < tmp.length; i++)
					System.out.println(tmp[i]);
				return false;
			}
			String ip = tmp[0];
			int port = Integer.valueOf(tmp[1]);
			
			//send STOR command
			//check if the file is exist
			File file = new File(path);
			if(!file.exists() || file.isDirectory())
			{
				System.out.println(path+" do not exists");
				return false;
			}
			
			this.response("STOR "+file.getName());
			System.out.println("server sockaddr:"+ip+","+port);
			//connect to server's data socket
		    datasocket = new Socket(ip,port);
			datasocket.setSoTimeout(timeout);
		    datareader = new DataInputStream(new FileInputStream(path));
			datawriter = new DataOutputStream(datasocket.getOutputStream());
			//wait for "150 Opening ASCII mode data connection."
			cmd = this.cmdreader.readLine();
			print(cmd);
			if(!cmd.startsWith("150")) 
			{
				return false;
			}
			
			//begin read data and write to the local file
			 byte [] buf = new byte[40800];
			 int length = 0;
            while((length = datareader.read(buf)) != -1)
            {
            	datawriter.write(buf, 0, length);
            	datawriter.flush();
            	
            }
            datawriter.close();
			cmd = this.cmdreader.readLine(); 
			print(cmd);
			if(!cmd.startsWith("226")) 
			{	
				return false;
			}
			
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return false;
		}
		finally
		{
			try{	
				if(dataSocket != null )dataSocket.close();
				if(datawriter != null )datawriter.close();
				if(datareader != null )datareader.close();
				} catch (IOException e1) {e1.printStackTrace();}
		}
		
		return stat;
	}
	
	
	/**
	 * send CWD command
	 * @param path
	 * @return
	 */
	synchronized public boolean cwd(String path)
	{
	     try 
	     {
	    	if(!curdir.endsWith("/"))
	    	    curdir+="/"+path;
	    	else curdir+="/"+path;
	    	
			response("CWD "+path);
			
			String cmd = this.cmdreader.readLine();
			print(cmd);
			if(!cmd.startsWith("250")) return false;
		 } catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}	
		return true;
	}
	
	
	/**
	 * send CDUP command 
	 * @return
	 */
	synchronized public boolean cdup()
	{
	     try 
	     {
			 if(this.curdir!=null && !this.curdir.equals("/"))
			 {
			 	  int index = curdir.lastIndexOf("/");
			 	  if(index > 0)
			 	  curdir = curdir.substring(0,index);
			 }

			this.response("CDUP");
			
			String cmd = this.cmdreader.readLine();
			print(cmd);
			if(!cmd.startsWith("200")) return false;
		 } catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}	
		return true;
	}
	
	/**
	 * do pasv command
	 * @return
	 */
	synchronized public String pasv()
	{
		try
		{
			String cmd = null;
			String param = null;
			
			//send the pasv command
			this.response("PASV");
			
			//------deal with the ip and port of data socket ----
			//  Entering Passive Mode (172,20,46,10,204,51)
			cmd = this.cmdreader.readLine();
			print(cmd);
			if(cmd ==null || !cmd.startsWith("227"))
				return null;
			
			//get the ip and port of the dataSocket of Server
			int start =  cmd.indexOf(new String("(".getBytes(),"GBK"));
			int end   =  cmd.indexOf(new String(")".getBytes(),"GBK"));		
			if(end < start || start <= 0 || end <= 0)
				return null;
			cmd = cmd.substring(start+1,end);
			String []tmp = cmd.replaceAll(" ", "").split(",");	
			//the params is wrong
			if(tmp.length != 6 ) return null;
			
			//get the ip and port of the client to conduct the data transform
			String ip = tmp[0]+"."+tmp[1]+"."+tmp[2]+"."+tmp[3];
			int port1 = Integer.valueOf(tmp[4]);
			int port2 = Integer.valueOf(tmp[5]);
			int port = port1*256 + port2;
			
			return ip.trim()+" "+port;
	    }
		catch(Exception e)
		{
			e.printStackTrace();
			return null;
		}
		
	}
	
	private void print(String msg)
	{
		 System.out.println("# server:"+this.serverIp +":"+this.serverPort+ "=>"+msg);
	}
	

	
	/**
	 * respone message to Server
	 * @param msg  
	 * @throws Exception
	 */
	private void response(String msg) throws Exception
	{
		this.cmdwriter.write(msg);
		this.cmdwriter.newLine();
		this.cmdwriter.flush();
	}
	
	public String getLocalPath() {
		return localPath;
	}
	public void setLocalPath(String localPath) {
		this.localPath = localPath;
	}
	public String getServerIp() {
		return serverIp;
	}
	public void setServerIp(String serverIp) {
		this.serverIp = serverIp;
	}
	public String getServerPort() {
		return serverPort;
	}
	public void setServerPort(String serverPort) {
		this.serverPort = serverPort;
	}


	/**
	 * check the current state
	 * @return
	 */
	public boolean isAlive()
	{
		if(this.cmdSocket == null || this.cmdSocket.isClosed() || !this.cmdSocket.isConnected())	
			return false;
		return true;
	}

	/**
	 * release the resouce
	 */
    public void close()
    {
    	try
    	{
			if(dataSocket != null ) {dataSocket.close(); dataSocket = null;}
			if(cmdSocket != null )  {cmdSocket.close();cmdSocket = null;}
		}
    	catch(Exception e)
		{
			e.printStackTrace();	
		}
    }
	
	static public void main(String [] args)
	{
		AFtpClient client = new AFtpClient(0,null,null);
		  try 
		  {
			client.getLogin();
			client.pasvList(null);
			BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
			while(true)
			{
				System.out.print("#:");
				String cmd = bf.readLine();
				if(cmd == null || cmd.trim().length() == 0)
					continue;
				
				 String param = AFtpUtil.phraseCommandParam(cmd.trim());
				 if(cmd.startsWith("cdup")) 
				 {
				    	client.cdup();
				    	client.pasvList(null);
				 }
				 else if(cmd.startsWith("cd")) 
			    {
			    	client.cwd(param); 
			    	client.pasvList(null);
			    }
				 else if(cmd.startsWith("retr"))
				 {
					 client.pasvRETR(param);
				 }
				 else if(cmd.startsWith("stor"))
				 {
					 client.pasvSTOR(param);
				 }
			}
		
		    } catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	}
}
